﻿/**************************************************************************************
 * KillPlugin
 * Deletes a plugin from the Dynamics CRM database
 *
 * @copyright    Copyright (c) 2013, CRM Accelerators (http://www.crmaccelerators.net)
 * @license      Microsoft Public License (Ms-PL)
 * @developer    Mitch Milam (http://blogs.infinite-x.net)
 * @version      1.0
 *
 * THIS SOFTWARE IS PROVIDED BY CRM Accelerators ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL CRM Accelerators BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **************************************************************************************/

#region

using System;
using CommandLine;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

#endregion

namespace KillPlugin
{
    class Program
    {
        private static string CrmServerUrl { get; set; }
        private static string UserName { get; set; }
        private static string Password { get; set; }
        private static string DomainName { get; set; }
        private static string PluginName { get; set; }

        static void Main(string[] args)
        {
            var options = new CommandLineOptions();

            if (!Parser.Default.ParseArguments(args, options))
            {
                return;
            }

            CrmServerUrl = options.CrmServerUrl;
            UserName = options.UserName;
            Password = options.Password;
            DomainName = options.DomainName;
            PluginName = options.PluginName;

            //if (options.Verbose)
            //{
            //    // Console.WriteLine("Filename: {0}", options.InputFile);
            //} 

            Utility.UpdateStatus("Connecting to CRM...", false);
             
            try
            {
                var connection = CrmConnection.Parse(Utility.BuildConnectionString(UserName, Password, DomainName, CrmServerUrl));

                using (var service = new OrganizationService(connection))
                {
                    Utility.UpdateStatus(string.Empty, true);

                    PluginName = CleanPluginName(PluginName);

                    var pluginTypes = GetPluginTypes(PluginName, service);

                    if (pluginTypes.Entities.Count == 0)
                    {
                        Utility.UpdateStatus(string.Empty, true);
                        Utility.UpdateStatus(string.Format("Unable to find a plugin with the name: {0}.", PluginName), true);

                        return;
                    }

                    Utility.UpdateStatus("Deleting plugin steps...", false);

                    DeletePluginTypes(pluginTypes, service);

                    Utility.UpdateStatus("Done.", true);
                    Utility.UpdateStatus("Deleting plugin assembly...", false);

                    DeletePluginAssembly(pluginTypes, service);

                    Utility.UpdateStatus("Done.", true);
                    Utility.UpdateStatus("Operation complete.", true);
                }

            }
            catch (Exception ex)
            {
                Utility.UpdateStatus(string.Empty, true);
                Utility.UpdateStatus(Utility.HandleExceptions(ex), true);
             }
        }

        private static void DeletePluginAssembly(EntityCollection pluginTypes, IOrganizationService service)
        {
            var pluginAssembly = pluginTypes.Entities[0].GetAttributeValue<EntityReference>("pluginassemblyid");

            if (pluginAssembly != null)
            {
                service.Delete(pluginAssembly.LogicalName, pluginAssembly.Id);
            }
        }

        private static void DeletePluginTypes(EntityCollection pluginTypes, IOrganizationService service)
        {
            foreach (var pluginType in pluginTypes.Entities)
            {
                DeletePluginSteps(service, pluginType);

                service.Delete(pluginType.LogicalName, pluginType.Id);
            }
        }

        private static void DeletePluginSteps(IOrganizationService service, Entity pluginType)
        {
            var pluginSteps = GetPluginSteps(service, pluginType);

            if (pluginSteps.Entities.Count <= 0)
            {
                return;
            }

            foreach (var pluginStep in pluginSteps.Entities)
            {
                service.Delete(pluginStep.LogicalName, pluginStep.Id);
            }
        }

        private static EntityCollection GetPluginSteps(IOrganizationService service, Entity pluginType)
        {
            var pluginStepQuery = new QueryExpression
            {
                EntityName = SdkMessageProcessingStep.EntityLogicalName,
                Criteria =
                {
                    FilterOperator = LogicalOperator.And,
                    Conditions =
                                {
                                    new ConditionExpression
                                        {
                                            AttributeName = "plugintypeid",
                                            Operator = ConditionOperator.Equal,
                                            Values = {pluginType.Id}
                                        },
                                }
                }
            };

            return service.RetrieveMultiple(pluginStepQuery);
        }

        private static EntityCollection GetPluginTypes(string pluginTypeName, IOrganizationService service)
        {
            var pluginQuery = new QueryByAttribute(PluginType.EntityLogicalName)
            {
                ColumnSet = new ColumnSet(new[] { "pluginassemblyid" })
            };

            pluginQuery.AddAttributeValue("assemblyname", pluginTypeName);

            return service.RetrieveMultiple(pluginQuery);
        }

        public static string CleanPluginName(string value)
        {
            return value.Replace(".dll", string.Empty);
        }
    }
}
